

#include <Wt/WText>
#include <Wt/WLayout>
#include <Wt/WTable>
#include <Wt/WLabel>
#include <Wt/WLineEdit>
#include <Wt/WCssDecorationStyle>
#include <Wt/WCssStyleSheet>
#include <Wt/WColor>
#include <Wt/WPushButton>
#include <boost/lexical_cast.hpp>
#include <Wt/WDate>
#include <Wt/WDatePicker>
#include <Wt/WTime>
#include <Wt/WTabWidget>
#include <Wt/WStackedWidget>
#include <Wt/WCheckBox>
#include <Wt/WApplication>
#include <Wt/WHBoxLayout>
#include <Wt/WVBoxLayout>

#include <Wt/Chart/WCartesianChart>
#include <Wt/Chart/WDataSeries>
#include <Wt/WAbstractItemModel>
#include <Wt/WStandardItemModel>
#include <Wt/WStandardItem>
#include <Wt/WWidget>
#include <Wt/WMenuItem>
#include <Wt/WMessageBox>
#include <fstream>

#include <QVector>
#include <QDate>
#include "DbSession.h"
//#include "User.h"
#include "Weight.h"
#include "DietPlan.h"
#include "PersonalWidget.h"
#include "PlanWidget.h"
/*#define max(X<Y) \
   ({typeof (a) _a=(a);\
       typeof (b) _b=(b);\
       _a > _b ? _a : _b;})*/

#ifndef NOMINMAX
#ifndef max
#define max(a,b) (((a)>(b)) ? (a):(b))
#endif
#endif /*NOMINMAX*/

using namespace Wt::Chart;

PersonalWidget::PersonalWidget(int id,WApplication *app, bool Other, int guestId):
        WContainerWidget(),other(Other),UserId(id),Db(DbSession::getInstance()),
   UserObject(Db.getUser(UserId)),UserWeights (Db.getWeights(UserId)),
   Users(Db.getAllUsers())
   {
      //Weight and other user info should be recieved from database
   WTable *layout = new WTable();
   layout->setId("strange table");
   addWidget(layout);
   if (!other)
   {
       new WLabel("Your name is ", layout->elementAt(0, 0));
       WLabel *Usernm = new WLabel(WString(UserObject->getLogin().toStdWString()), layout->elementAt(0, 0));
       Usernm->decorationStyle().font().setSize(WFont::Large);
   }
   else
   {
       //new WLabel("User name is ", layout->elementAt(0, 0));
   }

   WString  StrDate=WDate::currentDate().toString("dddd d MMMM yyyy");
   new WLabel("Today is ", layout->elementAt(1, 0));
   WText *Date = new WText(StrDate, layout->elementAt(1, 0));
   Date->decorationStyle().font().setSize(WFont::Medium);

   WString weightStr;
   if (not UserWeights->isEmpty())
   {
       QSharedPointer<Weight> CurrentWeight = UserWeights->at(UserWeights->size()-1);
       weightStr = boost::lexical_cast<std::string>(CurrentWeight->getWeight());
   }
   else
   {
     weightStr=WString("not defined yet");
   }

   if (!other)
   {
       new WLabel("Your current weight is  ", layout->elementAt(2, 0));
   }
   else
   {
       new WLabel("User's current weight is  ", layout->elementAt(2, 0));
   }
   CurWeight = new WText(weightStr, layout->elementAt(2, 0));
   CurWeight->decorationStyle().font().setSize(WFont::Large);
   CurWeight->setMargin(7, Top);

   if (!other)
   {
       WeightEdit = new WLineEdit(layout->elementAt(3, 0));
       WeightEdit->resize(WLength(20), WLength::Auto);
       WPushButton *AddWeightBtn = new WPushButton("Add weight", layout->elementAt(3, 0));
       AddWeightBtn->setMargin(10,Left);
       AddWeightBtn->clicked().connect(this, &PersonalWidget::addWeight);
   }



    tabmenu=new WTabWidget(layout->elementAt(4, 0));
    //layout->elementAt(5, 0)->addWidget(tabmenu);
    WeightWidg=WeightWidget();
    //w->decorationStyle().setBackgroundColor(WColor(255,191,128,128));
  tabmenu->setStyleClass("tabwidget");
  tabmenu->addTab(WeightWidg,"Weight");

  if (!other)
  {
      dietPlanWidget = new PlanWidget(UserId, PlanWidget::DIET);
      fitnessPlanWidget = new PlanWidget(UserId, PlanWidget::FITNESS);
  }
  else
  {
      dietPlanWidget = new PlanWidget(UserId, PlanWidget::DIET, 0, true, guestId);
      fitnessPlanWidget = new PlanWidget(UserId, PlanWidget::FITNESS, 0, true, guestId);
  }

  tabmenu->addTab(dietPlanWidget,"Diet");
  tabmenu->addTab(fitnessPlanWidget,"Fitness");
  tabmenu->setInternalPathEnabled();
  app->instance()->styleSheet().addRule(".tabwidget","border-bottom: 1px solid gray; padding-bottom: 5px;");

    //Weight and other user info should be recieved from database

//    /* Here we will add all main widgets and layouts after their creation */
//    WVBoxLayout *mainLayout = new WVBoxLayout();

//    /* Create labels with name */
//    if (!other)
//    {
//        /* create two labels and put then in hor..l layout */
//        WHBoxLayout *nameLayout = new WHBoxLayout();
//        nameLayout->addWidget(new WLabel("Your name is "));
//        WLabel *Usernm = new WLabel(WString(UserObject->getLogin().toStdWString()));
//        Usernm->decorationStyle().font().setSize(WFont::Large);
//        nameLayout->addWidget(Usernm);
//        nameLayout->addStretch(2);
//        mainLayout->addItem(nameLayout);
//    }
//    else
//    {
//        /* Name will be shown in ComboBox */
//    }

//    /* Creating labels with date */
//    WHBoxLayout *dateLayout = new WHBoxLayout();
//    dateLayout->addWidget(new WLabel("Today is "));
//    WString  StrDate=WDate::currentDate().toString("dddd d MMMM yyyy");
//    WText *Date = new WText(StrDate);
//    Date->decorationStyle().font().setSize(WFont::Medium);
//    dateLayout->addWidget(Date);
//    dateLayout->addStretch(2);
//    mainLayout->addItem(dateLayout);

//    /* Lets show user's weight */
//    WHBoxLayout *weightLayout = new WHBoxLayout();
//    WString weightStr;
//    if (not UserWeights->isEmpty())
//    {
//        QSharedPointer<Weight> CurrentWeight = UserWeights->at(UserWeights->size()-1);
//        weightStr = boost::lexical_cast<std::string>(CurrentWeight->getWeight());
//    }
//    else
//    {
//        weightStr=WString("not defined yet");
//    }
//    if (!other)
//    {
//        weightLayout->addWidget(new WLabel("Your current weight is  "));
//    }
//    else
//    {
//        weightLayout->addWidget(new WLabel("User's current weight is  "));
//    }
//    CurWeight = new WText(weightStr);
//    CurWeight->decorationStyle().font().setSize(WFont::Large);
//    CurWeight->setMargin(7, Top);
//    weightLayout->addWidget(CurWeight);
//    mainLayout->addItem(weightLayout);

//    /* Ok, now, if this page is looked by its owner then
//       he should have the possibiliy to change his weight */
//    if (!other)
//    {
//        WHBoxLayout *weightEditLayout = new WHBoxLayout();
//        WeightEdit = new WLineEdit();
//        WeightEdit->resize(WLength(20), WLength::Auto);
//        weightEditLayout->addWidget(WeightEdit);
//        WPushButton *AddWeightBtn = new WPushButton("Add weight");
//        AddWeightBtn->setMargin(10,Left);
//        AddWeightBtn->clicked().connect(this, &PersonalWidget::addWeight);
//        weightEditLayout->addWidget(AddWeightBtn);
//        mainLayout->addItem(weightEditLayout);
//    }

//    /* I hope there is no so much widgets left ...
//       Ok, adding tabmenu with graphic and plans */
//    tabmenu=new WTabWidget();
//    //layout->elementAt(5, 0)->addWidget(tabmenu);
//    WeightWidg=WeightWidget();
//    //w->decorationStyle().setBackgroundColor(WColor(255,191,128,128));
//    tabmenu->setStyleClass("tabwidget");
//    tabmenu->addTab(WeightWidg,"Weight");
//    if (!other)
//    {
//        dietPlanWidget = new PlanWidget(UserId, PlanWidget::DIET);
//        fitnessPlanWidget = new PlanWidget(UserId, PlanWidget::FITNESS);
//    }
//    else
//    {
//        dietPlanWidget = new PlanWidget(UserId, PlanWidget::DIET, 0, true, guestId);
//        fitnessPlanWidget = new PlanWidget(UserId, PlanWidget::FITNESS, 0, true, guestId);
//    }
//    tabmenu->addTab(dietPlanWidget,"Diet");
//    tabmenu->addTab(fitnessPlanWidget,"Fitness");
//    tabmenu->setInternalPathEnabled();
//    //mainLayout->addWidget(tabmenu);

//    this->setLayout(mainLayout);
//    app->instance()->styleSheet().addRule(".tabwidget","border-bottom: 1px solid gray; padding-bottom: 5px;");
}

PersonalWidget::~PersonalWidget()
{
    delete dietPlanWidget;
    delete fitnessPlanWidget;
    delete WeightWidg;
}

WWidget *PersonalWidget::WeightWidget(){

    WContainerWidget *w2=new WContainerWidget();
    WContainerWidget *w1=new WContainerWidget();
    WContainerWidget *w3=new WContainerWidget();
    WCheckBox *userChBox;
      if (not CheckBoxList.isEmpty())
      {
        CheckBoxList.clear();
      }
      for (int s=0;s<Users->size();++s)
      {
        userChBox=new WCheckBox(WString(Users->at(s)->getLogin().toStdString()),w2);
        userChBox->setMargin(15,Left);
        CheckBoxList.push_back(userChBox);
        if (Users->at(s)->getId()==UserId)
        {
          CheckBoxList.last()->setChecked();
        }
        CheckBoxList.last()->changed().connect(this,&PersonalWidget::UpdateChart);
         new WBreak(w2);
    }
    wdp1=new WDatePicker(w3);
    wdp2=new WDatePicker(w3);
    new WBreak(w3);

    wdp1->setMargin(10,Right);
    wdp1->setFormat("d.M.yy");
    wdp1->setDate(WDate::currentDate().addMonths(-4));
    wdp1->lineEdit()->changed().connect(this,&PersonalWidget::UpdateChart);
    wdp2->setFormat("d.M.yy");
    wdp2->setDate(WDate::currentDate());
    wdp2->lineEdit()->changed().connect(this,&PersonalWidget::UpdateChart);



    w=new WContainerWidget();
    WDate begin=wdp1->date();
    WDate end=wdp2->date();
    model = MakeModel(w,QDate(begin.year(),begin.month(),begin.day()),QDate(end.year(),end.month(),end.day()));
    WeightChart = MakeChart(w);
    w1->addWidget(w);
    w1->addWidget(w3);
    if (!other)
    {
      w1->addWidget(w2);
    }

    return w1;
}

WCartesianChart *PersonalWidget::MakeChart(WContainerWidget *parent){
    WCartesianChart *Chart = new WCartesianChart(parent);

    Chart->setTitle("Weight dynamic");
    Chart->setModel(model);
    Chart->setXSeriesColumn(0);
    Chart->setLegendEnabled(true);
    Chart->setType(ScatterPlot);
    Chart->axis(XAxis).setScale(DateScale);
    Chart->axis(XAxis).setLabelFormat("d.M.yy");
    Chart->axis(XAxis).setLabelInterval(10.0);


    Chart->setPlotAreaPadding(100, Right);
    Chart->setPlotAreaPadding(30, Top | Bottom);
    AddDataSeriesOnChart(Chart);

    Chart->resize(800, 350); // WPaintedWidget must be given explicit size

    Chart->setMargin(10, Top | Bottom);            // add margin vertically
    Chart->setMargin(WLength::Auto, Left | Right); // center horizontally
return Chart;
}

WAbstractItemModel *PersonalWidget::MakeModel(WContainerWidget *parent,QDate begin,QDate end){

    //Make vector of Dates
  /*  int iii;
    QVector<QDate> AllDates(0);
    QSharedPointer<QVector<QSharedPointer<Weight> > > UserWeights;
    Users=Db.getAllUsers();
    for (int s=0;s<Users->size();++s){
     if (CheckBoxList.at(s)->isChecked())
     {

      UserWeights = Users->at(s)->getWeights();
      for (int ss=0;ss<UserWeights->size();++ss){
        QDate d=UserWeights->at(ss)->getDate();
        iii=0;
        if (AllDates.isEmpty())
        {
          AllDates.push_back(d);
        }
        else
        {
         while (iii<AllDates.size())
         {
           if (AllDates.at(iii)>=d)
           {
             if (AllDates.at(iii)!=d)
             {
                AllDates.insert(iii,1,d);
             }
             iii=AllDates.size();
           }
           else
           {
             iii=iii+1;
           }
         }
        if (d>AllDates.at(iii-1))
        {
         AllDates.push_back(d);
        }
        }
        }
     }
  }
WStandardItemModel *model = new WStandardItemModel(AllDates.size()+1,Users->size()+1,parent);
model->setHeaderData(0,boost::any(Wt::WString::fromUTF8("Date")));

for (int k=0;k<AllDates.size();++k){
  QDate D=AllDates.at(k);
  model->setData(k+1,0,boost::any(WDate(D.year(),D.month(),D.day())));
}

//Fill LineSeries with data

for (int s=0;s<Users->size();++s)
{
 if (CheckBoxList.at(s)->isChecked())
 {
  model->setHeaderData(s+1,boost::any
   (WString(Users->at(s)->getLogin().toStdString())));
  UserWeights = Users->at(s)->getWeights();
  iii=0;
  if (not UserWeights->isEmpty())
  {
    int Dn=UserWeights->at(0)->getDate().dayOfYear();
    double Wn=UserWeights->at(0)->getWeight();
    while (AllDates.at(iii).dayOfYear()<Dn){ iii=iii+1;}
    model->setData(iii+1,s+1,boost::any(Wn));
    iii=iii+1;
    for (int ss=1;ss<UserWeights->size();++ss)
    {
     int Dk=UserWeights->at(ss)->getDate().dayOfYear();
     double Wk=UserWeights->at(ss)->getWeight();
     while (AllDates.at(iii).dayOfYear()<Dk)
     {
       double W=((Wn-Wk)/((double)Dn-Dk)*AllDates.at(iii).dayOfYear()+((Dn*Wk-Wn*Dk)/(Dn-Dk)));
       model->setData(iii+1,s+1,boost::any(W));
       iii=iii+1;
     }
     if (AllDates.at(iii).dayOfYear()==Dk)
     {
         model->setData(iii+1,s+1,boost::any(Wk));
         iii=iii+1;
     }
     Wn=Wk; Dn=Dk;
   }
   }
 }
}*/

    int iii;
       QSharedPointer<QVector<QSharedPointer<User> > > Users;
       QVector<QDate> AllDates(0);
           AllDates.push_back(begin);
           AllDates.push_back(end);
       QSharedPointer<QVector<QSharedPointer<Weight> > > UserWeights;
       Users=Db.getAllUsers();
           for (int s=0;s<Users->size();++s)
           {
         UserWeights = Users->at(s)->getWeights();
         for (int ss=0;ss<UserWeights->size();++ss)
         {
          if (CheckBoxList.at(s)->isChecked())
          {
           QDate d=UserWeights->at(ss)->getDate();
                   if ((d>begin) && (d<end))
                   {
             iii=0;
             {
            while (iii<AllDates.size())
             if (AllDates.at(iii)>=d)
              {
                if (AllDates.at(iii)!=d)
                {
                   AllDates.insert(iii,1,d);
                }
                iii=AllDates.size();
              }
              else
              {
                iii=iii+1;
              }
             }
             if (d>AllDates.at(iii-1))
             {
               AllDates.push_back(d);
             }
           }
         }
        }
       }


   WStandardItemModel *model = new WStandardItemModel(AllDates.size()+1,Users->size()+1,parent);
   model->setHeaderData(0,boost::any(Wt::WString::fromUTF8("Date")));

   for (int k=0;k<AllDates.size();++k){
     QDate D=AllDates.at(k);
     model->setData(k+1,0,boost::any(WDate(D.year(),D.month(),D.day())));
   }

   //Fill LineSeries with data

   for (int s_=0;s_<Users->size();++s_)
    {
    if (CheckBoxList.at(s_)->isChecked())
    {
     model->setHeaderData(s_+1,boost::any
      (WString(Users->at(s_)->getLogin().toStdString())));
     UserWeights = Users->at(s_)->getWeights();
     if (not UserWeights->isEmpty())
     {
       if (not ((UserWeights->at(0)->getDate()>end)|
           (UserWeights->at(UserWeights->size()-1)->getDate()<begin)))
       {
           int s=0;
                   while ((UserWeights->at(s)->getDate()<begin) && (s<UserWeights->size()))
                   {
                     s=s+1;
                   }
                   if ((s!=0) && (UserWeights->at(s)->getDate()>begin))
                   {
                     s=s-1;
                   }
                   QDate Dn_=UserWeights->at(s)->getDate();
                   int Dn=Dn_.dayOfYear();
                   double Wn=UserWeights->at(s)->getWeight();

                   iii=0;

                   while ((iii<AllDates.size()) && (AllDates.at(iii)<max(begin,Dn_)))
                   {
                     iii=iii+1;
                   }
                   if (UserWeights->at(s)->getDate()>begin)
                   {
                     model->setData(iii+1,s_+1,boost::any(Wn));
                   }
          // iii=iii+1;
           int ss=s+1;
           while ((ss<UserWeights->size()) && ((Dn_<end)))
           {
            QDate Dk_=UserWeights->at(ss)->getDate();
            int Dk=Dk_.dayOfYear();
            double Wk=UserWeights->at(ss)->getWeight();
            while ((iii<AllDates.size())&&(AllDates.at(iii)<=Dk_) && (AllDates.at(iii)<=end))
            {
              double W=((Wn-Wk)/((double)Dn-Dk)*AllDates.at(iii).dayOfYear()+((Dn*Wk-Wn*Dk)/(Dn-Dk)));
              model->setData(iii+1,s_+1,boost::any(W));
              iii=iii+1;
            }/*
            if (AllDates.at(iii)==Dk_)
            {
                model->setData(iii+1,s_+1,boost::any(Wk));
                iii=iii+1;
            }*/
            Wn=Wk;
            Dn=Dk;
            Dn_=Dk_;
            ss=ss+1;
           }
       }
    }
   }
   }
    return model;
}


void PersonalWidget::addWeight()
{
    WString tmpW = WeightEdit->text();
    double neww=boost::lexical_cast<double>(tmpW.toUTF8().c_str());
    Db.addWeight(UserId,neww);
    CurWeight->setText(tmpW);
    WeightEdit->setText("");
    UserWeights = Db.getWeights(UserId);
    UpdateChart();/*
    WMessageBox::show("diet", WString("W1 = ") +
                      WString(QString::number(W1).toStdString()) , Ok | Cancel);*/

}
void PersonalWidget::UpdateChart(){

    WDate begin=wdp1->date();
    WDate end=wdp2->date();
    if (end<begin)
    {
        begin=end;
        end=wdp1->date();
    }

    delete(WeightChart);
    delete(model);

    model=MakeModel(w,QDate(begin.year(),begin.month(),begin.day()),QDate(end.year(),end.month(),end.day()));
    WeightChart=MakeChart(w);
}

void PersonalWidget::AddDataSeriesOnChart(WCartesianChart *Chart){
    for (int s=1;s<=Users->size();++s)
    {
     if (CheckBoxList.at(s-1)->isChecked())
        {
         Chart->addSeries(WDataSeries(s, CurveSeries));
         if (Users->at(s-1)->getWeights()->size()==1)
         {
             Chart->series(s).setMarker(CircleMarker);
             Chart->series(s).setMarkerSize(5);
         }

     }
    }
}
